<?php
/**
 * Created by PhpStorm.
 * User: DSPC
 * Date: 2019/1/8
 * Time: 19:34
 */

namespace App\Utils;


class Tree
{
    use Singleton;

    // 用于树型数组完成递归格式的全局变量
    private $formatTree;

    /**
     * 将数据格式化为带有层次结构的数据集
     * @param array $lists 要转换的数据集
     * @param string $pk 数据集主键字段
     * @param string $pid 父级主键字段
     * @param string $children 标记字段键名
     * @param int $level 栏目级别
     * @return array
     */
    public function toFormatList(array $lists, $pk = 'id', $pid = 'pid', $children = 'children', $level = 1){
        if (count($lists) > 0){
            $lists = $this->toTree($lists, $pk, $pid, $children, false);
            $this->formatTree = [];
            $this->_toFormatTree($lists, $children, $level);
            return $this->formatTree;
        } else {
            return array();
        }
    }

    /**
     * 将数组转换为树状结构
     * @param array $lists 要转换的数据集
     * @param string $pk 栏目主键字段
     * @param string $pid 父级主键字段
     * @param string $children 标记字段键名
     * @param bool $top 只返回顶级栏目
     * @return array
     */
    public function toTree(array $lists, $pk = 'id', $pid = 'pid', $children = 'children', $top = true){
        $tree = array();
        $refer = array();
        // 创建基于组件的数组引用
        foreach($lists as $key => $item){
            $refer[$item[$pk]] =& $lists[$key];
        }
        // 转换为树状结构数组
        foreach($lists as $key => $item){
            $parent_id = $item[$pid];
            // 判断是否存在父级栏目
            if (array_key_exists($parent_id, $refer)){
                if (isset($refer[$parent_id])){
                    $parent =& $refer[$parent_id];
                    $parent[$children][] =& $lists[$key];
                }
            } else {
                if (!$top || $lists[$key]['pid'] == 0){
                    $tree[] =& $lists[$key];
                }
            }
        }
        return $tree;
    }

    /**
     * 获取指定栏目下的子栏目
     * @param array $lists 数据集
     * @param integer $id 栏目ID
     * @param string $pk 数据集主键字段
     * @param string $pid 父级主键字段
     * @param string $children 标记字段键名
     * @param array $data 递归和最终返回的数据集
     * @return array
     */
    public function toTreeChildren(array $lists, int $id, $pk = 'id', $pid = 'pid', $children = 'children', $data = array()){
        if (count($lists) > 0){
            foreach ($lists as $key => $item){
                $_temp = (array)$item;
                if ($_temp[$pid] == $id) {
                    $data[$_temp[$pk]] = $item;
                    $data = $this->toTreeChildren($lists, $_temp[$pk], $pk, $pid, $children, $data);
                };
            }
            return $data;
        } else {
            return [];
        }
    }

    /**
     * 树状结构格式化数组
     * @param array $lists
     * @param string $children
     * @param int $level
     * @return array
     */
    public function treeFormat(array $lists, $children = 'children', $level = 0){
        if (count($lists) > 0){
            $this->formatTree = array();
            $this->_toFormatTree($lists, $children, $level);
            return $this->formatTree;
        } else {
            return array();
        }
    }

    /**
     * 递归将数组格式化为带有层次结构的数组
     * @param array $treeList 树状结构数据集
     * @param string $children 标记字段键名
     * @param int $level 栏目级别
     * @return bool
     */
    private function _toFormatTree(array $treeList, $children = 'children', $level = 0){
        foreach($treeList as $key => $item){
//            if (is_numeric($level)){
//                $item['level'] = $level;
//            }
            if (array_key_exists($children, $item)){
                $_temp = $item[$children];
                unset($item[$children]);
                array_push($this->formatTree, $item);
                $this->_toFormatTree($_temp, $children, ($level + 1));
            } else {
                array_push($this->formatTree, $item);
            }
        }
        return true;
    }

    /**
     * 获取父级关系
     * @param $id
     * @param array $list
     * @param string $pk
     * @param string $pid
     * @param null $field
     * @param array $data
     * @return array
     */
    public function toParent($id, array $list, $pk = 'id', $field = null, $pid = 'pid', $data = []){
        if ($id > 0){
            foreach ($list as $item){
                if ($item[$pk] == $id){
                    if ($item[$pid] > 0) {
                        $data = $this->toParent($item[$pid], $list, $pk, $field, $pid, $data);
                    }
                    $data[] = $field ? $item[$field] : $item;
                }
            }
        }
        return $data;
    }

    /**
     * 获取子级主键
     * @param int $id
     * @param string $key
     * @param array $data
     * @param string $pid
     * @param array $response
     * @return array
     */
    public function toChildrenId($id, $key = 'id', array $data = [], $pid = 'pid', $response = []){
        $response[] = (int)$id;
        foreach ($data as $item){
            if ($item[$pid] == $id){
                $response = $this->toChildrenId($item[$key], $key, $data, $pid, $response);
            }
        }
        return $response;
    }
}
